<template>
  <div class="flowGrid" v-if="easyFlowVisible">
    <div class="flex-row">
      <div class="input-block">
        <el-slider
          input-size="mini"
          :min="30"
          :max="100"
          v-model="scaleNum"
          show-input
          :show-input-controls="false"
        ></el-slider>
      </div>
      <!-- <div style="width:60px;padding:0 10px 0 20px;">
        <el-input :min="30" :max="100" size="mini" v-model.number="scaleNum"></el-input>
      </div>-->
      <div>%</div>
    </div>
    <div
      id="flowContainer"
      ref="flowContainer"
      @mousewheel="mouseScale($event)"
      class="container"
      v-flowDrag
    >
      <template v-for="node in data.nodeList">
        <flow-node
          :id="node.id"
          :key="node.id"
          :node="node"
          :activeNodeId="activeNodeId"
          @deleteLine="deleteLine"
          @deleteNode="deleteNode"
          @changeNodeSite="changeNodeSite"
          @clickNode="clickNode"
        ></flow-node>
      </template>
    </div>
  </div>
</template>

<script>
import { mapGetters, mapMutations } from "vuex";
import draggable from "vuedraggable";
import { jsPlumb } from "jsplumb";
import { config } from "@/components/flow/config";
import flowNode from "@/components/flow/node";
import lodash from "lodash";
export default {
  props: {
    flowData: {
      type: Object,
      default: function() {
        return {};
      }
    }
  },
  data() {
    return {
      // 删除节点id集合
      ids: [],
      // jsPlumb 实例
      jsPlumb: null,
      // 控制画布销毁
      easyFlowVisible: true,
      // 控制流程数据显示与隐藏
      flowInfoVisible: false,
      // 是否加载完毕标志位
      loadEasyFlowFinish: false,
      // 数据
      data: { nodeList: [], lineList: [] },
      // 激活的节点
      activeNodeId: undefined,
      scaleNum: 100,
      nodeId: ""
    };
  },
  // 一些基础配置移动该文件中
  mixins: [config],
  components: {
    draggable,
    flowNode
  },
  computed: {
    ...mapGetters(["getReadOnlyFlag"])
  },
  directives: {
    flowDrag: {
      bind(el, binding, vnode, oldNode) {
        if (!binding) {
          return;
        }
        el.onmousedown = e => {
          if (e.button == 2) {
            // 右键不管
            return;
          }
          //  鼠标按下，计算当前原始距离可视区的高度
          let disX = e.clientX;
          let disY = e.clientY;
          el.style.cursor = "move";

          document.onmousemove = function(e) {
            // 移动时禁止默认事件
            e.preventDefault();
            const left = e.clientX - disX;
            disX = e.clientX;
            el.scrollLeft += -left;

            const top = e.clientY - disY;
            disY = e.clientY;
            el.scrollTop += -top;
          };

          document.onmouseup = function(e) {
            el.style.cursor = "auto";
            document.onmousemove = null;
            document.onmouseup = null;
          };
        };
      }
    }
  },
  mounted() {
    this.jsPlumb = jsPlumb.getInstance();
  },
  watch: {
    scaleNum: {
      handler(value) {
        this.changeScale(value);
      },
      immediate: true
    },
    flowData: {
      handler(val) {
        if (val !== {}) {
          this.$nextTick(() => {
            this.dataReload(val);
          });
        }
      },
      immediate: true,
      deep: true
    }
  },
  methods: {
    ...mapMutations("flow", ["setFlowList"]),
    // 返回唯一标识
    getUUID() {
      return Math.random()
        .toString(36)
        .substr(3, 10);
    },
    changeScale(val) {
      let zoom = val / 100;
      let dom = this.$refs.flowContainer;
      if (this.jsPlumb && dom) {
        dom.style.transform = "scale(" + zoom + ")";
        this.jsPlumb.setZoom(zoom);
      }
      console.log(val);
    },
    mouseScale(e) {
      let delta =
        (e.wheelDelta && (e.wheelDelta > 0 ? 1 : -1)) || // chrome & ie &其它
        (e.detail && (e.detail > 0 ? -1 : 1)); // firefox
      if (delta > 0) {
        if (this.scaleNum > 100) {
          return false;
        } else {
          this.scaleNum = this.scaleNum + 5;
        }
      } else {
        if (this.scaleNum < 35) {
          return false;
        } else {
          this.scaleNum = this.scaleNum - 5;
        }
      }
    },
    jsPlumbInit() {
      this.jsPlumb.ready(() => {
        // 导入默认配置
        this.jsPlumb.importDefaults(this.jsplumbSetting);
        // 会使整个jsPlumb立即重绘。
        this.jsPlumb.setSuspendDrawing(false, true);
        // 初始化节点
        this.loadEasyFlow();
        // 单点击了连接线, https://www.cnblogs.com/ysx215/p/7615677.html
        this.jsPlumb.bind("click", (conn, originalEvent) => {
          if (this.getReadOnlyFlag) {
            return false;
          }
          this.activeNodeId = undefined;
          this.$confirm("确定删除所点击的线吗?", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning"
          })
            .then(() => {
              this.jsPlumb.deleteConnection(conn);
            })
            .catch(() => {});
        });
        // 连线
        this.jsPlumb.bind("connection", evt => {
          let from = evt.source.id;
          let to = evt.target.id;
          if (this.loadEasyFlowFinish) {
            let node = this.data.nodeList.find(item => {
              return item.id === from;
            });
            this.data.lineList.push({ from: from, to: to });
            this.setData();
            if (node) {
              let preNode = this.flowData.lineList.find(item => {
                return item.to === node.id;
              });
              if (preNode) {
                this.$set(node, "preNode", preNode.from);
              }
              // 返回连接源节点
              this.$emit("link", node);
            }
          }
        });

        // 删除连线回调
        this.jsPlumb.bind("connectionDetached", evt => {
          this.deleteLine(evt.sourceId, evt.targetId);
        });

        // 改变线的连接节点
        this.jsPlumb.bind("connectionMoved", evt => {
          this.changeLine(evt.originalSourceId, evt.originalTargetId);
        });

        // 连线
        this.jsPlumb.bind("beforeDrop", evt => {
          let from = evt.sourceId;
          let to = evt.targetId;
          if (from === to) {
            this.$message.error("节点不支持连接自己");
            return false;
          }
          if (this.hasLine(from, to)) {
            this.$message.error("该关系已存在,不允许重复创建");
            return false;
          }
          if (this.hashOppositeLine(from, to)) {
            this.$message.error("不支持两个节点之间连线回环");
            return false;
          }
          let node = this.data.nodeList.find(item => {
            return item.id === from;
          });
          if (node && node.valid) {
            if (node.type === "end") {
              this.$message.error("该节点为终止节点");
              return false;
            }
            // 目标为起始节点
            if (this.isStartNode(to)) {
              this.$message.error("起始节点，不可连接");
              return false;
            }
          } else {
            this.$message.error("该节点无效，不可连接");
            return false;
          }
          this.updateState(to, true);
          this.$message.success("连接成功");
          return true;
        });
        // beforeDetach
        this.jsPlumb.bind("beforeDetach", evt => {
          console.log("beforeDetach", evt);
        });
        this.jsPlumb.setContainer(this.$refs.flowContainer);
      });
    },
    // 加载流程图
    loadEasyFlow() {
      // 初始化节点
      for (var i = 0; i < this.data.nodeList.length; i++) {
        let node = this.data.nodeList[i];
        // 设置源点，可以拖出线连接其他节点
        this.jsPlumb.makeSource(node.id, this.jsplumbSourceOptions);
        // // 设置目标点，其他源点拖出的线可以连接该节点
        this.jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions);
        this.jsPlumb.draggable(node.id, {
          containment: "parent",
          stop: function(el) {
            console.log("停止拖拽", el);
          }
        });
      }
      // 初始化连线
      for (var i = 0; i < this.data.lineList.length; i++) {
        let line = this.data.lineList[i];
        this.jsPlumb.connect(
          {
            source: line.from,
            target: line.to,
            label: line.label ? line.label : ""
          },
          this.jsplumbConnectOptions
        );
      }
      this.$nextTick(function() {
        this.loadEasyFlowFinish = true;
      });
    },
    // 删除线
    deleteLine(from, to) {
      this.delLineAll(from, to);
    },
    delLineAll(from, to) {
      const vm = this;
      let index = -1;
      if (from) {
        index = vm.data.lineList.findIndex(item => {
          return item.from === from && item.to === to;
        });
      } else {
        index = vm.data.lineList.findIndex(item => {
          return item.from === to;
        });
      }
      if (index > -1) {
        let _to = vm.data.lineList[index].to;
        vm.ids.push(_to);
        vm.updateState(_to, false);
        vm.data.lineList.splice(index, 1);
        vm.delLineAll(null, _to);
      } else {
        vm.setData();
        vm.$emit("delete", vm.ids);
        vm.ids = [];
      }
    },
    // 改变连线
    changeLine(oldFrom, oldTo) {
      this.deleteLine(oldFrom, oldTo);
    },
    // 改变节点的位置
    changeNodeSite(data) {
      for (var i = 0; i < this.data.nodeList.length; i++) {
        let node = this.data.nodeList[i];
        if (node.id === data.nodeId) {
          node.left = data.left;
          node.top = data.top;
        }
      }
      this.setData();
    },
    randomNum(m, n) {
      var num = Math.floor(Math.random() * (m - n) + n);
      return num;
    },

    /**
     * 拖拽结束后添加新的节点
     * @param evt
     * @param nodeMenu 被添加的节点对象
     * @param mousePosition 鼠标拖拽结束的坐标
     */
    dragAddNode(evt, nodeMenu, mousePosition) {
      if (this.getReadOnlyFlag) {
        this.$message({
          message: "已采集不允许创建",
          type: "warning"
        });
        return false;
      }
      var screenX = evt.originalEvent.clientX,
        screenY = evt.originalEvent.clientY;
      let flowContainer = this.$refs.flowContainer;
      var containerRect = flowContainer.getBoundingClientRect();
      var left = screenX,
        top = screenY;
      // 计算是否拖入到容器中
      if (
        left < containerRect.x ||
        left > containerRect.width + containerRect.x ||
        top < containerRect.y ||
        containerRect.y > containerRect.y + containerRect.height
      ) {
        if (nodeMenu.type === "start") {
          vm.$store.commit("setHasPoint", false);
        } else if (nodeMenu.type === "end") {
          vm.$store.commit("setOutPoint", false);
        }
        this.$message.error("请把节点拖入到画布中");
        return;
      }
      left = left - containerRect.x + flowContainer.scrollLeft;
      top = top - containerRect.y + flowContainer.scrollTop;
      // 居中
      left -= 85;
      top -= 16;
      if (nodeMenu.type === "start") {
        nodeMenu.valid = true;
      } else if (nodeMenu.type === "center") {
        nodeMenu.valid = false;
      } else if (nodeMenu.type === "end") {
        nodeMenu.valid = false;
      }

      var nodeId = this.getUUID();
      // 动态生成名字
      var origName = nodeMenu.name;
      var nodeName = origName;
      var index = 1;
      while (index < 10000) {
        var repeat = false;
        for (var i = 0; i < this.data.nodeList.length; i++) {
          let node = this.data.nodeList[i];
          if (node.name === nodeName) {
            nodeName = origName + index;
            repeat = true;
          }
        }
        if (repeat) {
          index++;
          continue;
        }
        break;
      }
      nodeMenu.name = nodeName;
      var node = {
        id: nodeId,
        left: left + "px",
        top: top + "px",
        ...nodeMenu
      };
      /**
       * 这里可以进行业务判断、是否能够添加该节点
       */
      this.data.nodeList.push(node);
      this.$nextTick(function() {
        if (nodeMenu.type === "start") {
          this.$store.commit("setHasPoint", true);
          this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions);
        } else if (nodeMenu.type === "center") {
          this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions);
          this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions);
        } else if (nodeMenu.type === "end") {
          this.$store.commit("setOutPoint", true);
          this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions);
        }
        this.jsPlumb.draggable(nodeId, {
          containment: "parent"
        });
      });
      this.setData();
    },
    /**
     * 点击后添加新的节点
     * @param nodeMenu 被添加的节点对象
     */
    addNode(nodeMenu) {
      if (this.getReadOnlyFlag) {
        this.$message({
          message: "已采集不允许创建",
          type: "warning"
        });
        return false;
      }
      var left = 0,
        top = 0;
      let flowContainer = this.$refs.flowContainer;
      var containerRect = flowContainer.getBoundingClientRect();
      if (nodeMenu.type === "start") {
        left = this.randomNum(0, 200);
        top = this.randomNum(0, parseInt(containerRect.height - 200));
        nodeMenu.valid = true;
      } else if (nodeMenu.type === "center") {
        left = this.randomNum(
          parseInt(230),
          parseInt(containerRect.width - 400)
        );
        top = this.randomNum(0, parseInt(containerRect.height - 200));
        nodeMenu.valid = false;
      } else if (nodeMenu.type === "end") {
        left = this.randomNum(
          parseInt(containerRect.width - 300),
          parseInt(containerRect.width - 130)
        );
        top = this.randomNum(80, parseInt(containerRect.height - 200));
        nodeMenu.valid = false;
      }
      var nodeId = this.getUUID();
      // 动态生成名字
      var origName = nodeMenu.name;
      var nodeName = origName;
      var index = 1;
      while (index < 10000) {
        var repeat = false;
        for (var i = 0; i < this.data.nodeList.length; i++) {
          let node = this.data.nodeList[i];
          if (node.name === nodeName) {
            nodeName = origName + index;
            repeat = true;
          }
        }
        if (repeat) {
          index++;
          continue;
        }
        break;
      }
      nodeMenu.name = nodeName;
      var node = {
        id: nodeId,
        left: left + "px",
        top: top + "px",
        ...nodeMenu
      };
      /**
       * 这里可以进行业务判断、是否能够添加该节点
       */
      this.data.nodeList.push(node);
      this.$nextTick(function() {
        if (nodeMenu.type === "start") {
          this.$store.commit("setHasPoint", true);
          this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions);
        } else if (nodeMenu.type === "center") {
          this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions);
          this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions);
        } else if (nodeMenu.type === "end") {
          this.$store.commit("setOutPoint", true);
          this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions);
        }
        this.jsPlumb.draggable(nodeId, {
          containment: "parent"
        });
      });
      this.setData();
    },
    /**
     * 删除节点
     * @param node 被删除节点的对象
     */
    deleteNode(node) {
      const vm = this;
      vm.$confirm("确定要删除节点【" + node.name + "】？", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
        closeOnClickModal: false
      }).then(() => {
        /**
         * 这里需要进行业务判断，是否可以删除
         */
        let index = vm.data.nodeList.findIndex(item => {
          return item.id === node.id;
        });
        if (index > -1) {
          let id = node.id;
          vm.ids.push(id);
          let line = vm.data.lineList.find(item => {
            return item.to === id;
          });
          if (node.type === "start") {
            vm.$store.commit("setHasPoint", false);
          } else if (node.type === "end") {
            vm.$store.commit("setOutPoint", false);
          }
          vm.data.nodeList.splice(index, 1);
          // 删除线
          if (line) {
            vm.delLineAll(line.from, line.to);
          } else {
            vm.$emit("delete", vm.ids);
          }
        }
      });
    },
    clickNode(node) {
      if (node.valid) {
        this.activeNodeId = node.id;
        this.nodeId = node.id;
        let preNode = this.flowData.lineList.find(item => {
          return item.to === node.id;
        });
        if (preNode) {
          this.$set(node, "preNode", preNode.from);
        } else {
          this.$set(node, "preNode", null);
        }
        let nextNode = this.flowData.lineList.find(item => {
          return item.from === node.id;
        });
        if (nextNode) {
          this.$set(node, "nextNode", nextNode.to);
        } else {
          this.$set(node, "nextNode", null);
        }
        this.$emit("click", node);
      } else {
        this.$confirm("无有效接入源，请先连接关联线!", "提示", {
          confirmButtonText: "确定",
          type: "warning"
        });
        return false;
      }
    },
    // 更新节点可用状态
    updateState(to, valid) {
      // 判断节点是否连接后是否有效 改变状态
      let index = this.data.nodeList.findIndex(item => {
        return item.id === to;
      });
      if (index > -1) {
        let node = this.data.nodeList[index];
        node.valid = valid;
        this.$set(this.data.nodeList, index, node);
      }
    },
    isStartNode(id) {
      let node = this.data.nodeList.find(item => {
        return item.id === id;
      });
      if (node && node.type === "start") {
        return true;
      } else {
        return false;
      }
    },
    // 是否具有该线
    hasLine(from, to) {
      for (var i = 0; i < this.data.lineList.length; i++) {
        var line = this.data.lineList[i];
        if (line.from === from && line.to === to) {
          return true;
        }
      }
      return false;
    },
    // 是否含有相反的线
    hashOppositeLine(from, to) {
      return this.hasLine(to, from);
    },
    // 加载流程图
    dataReload(data) {
      let num = this.scaleNum;
      this.easyFlowVisible = false;
      this.$nextTick(() => {
        const vm = this;
        vm.easyFlowVisible = true;
        vm.data = { ...data };
        vm.$nextTick(() => {
          vm.jsPlumb = jsPlumb.getInstance();
          vm.$nextTick(() => {
            vm.jsPlumbInit();
            vm.changeScale(num);
          });
        });
      });
    },
    // 保存数据
    setData() {
      if (this.data !== {}) {
        this.setFlowList(this.data);
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.flowGrid {
  position: relative;
  width: 100%;
  height: 100%;
}
#flowContainer {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-image: url("../../assets/image/grid.png");
  background-repeat: no-repeat;
  background-size: cover;
}

.labelClass {
  background-color: white;
  padding: 5px;
  opacity: 0.7;
  border: 1px solid #346789;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.flow-tooltar {
  padding-left: 10px;
  box-sizing: border-box;
  height: 42px;
  line-height: 42px;
  z-index: 3;
  -webkit-box-shadow: 0 8px 12px 0 rgba(0, 52, 107, 0.04);
  box-shadow: 0 8px 12px 0 rgba(0, 52, 107, 0.04);
  border-bottom: 1px solid #dadce0;
}

.jtk-overlay {
  cursor: pointer;
  color: #4a4a4a;
}
.flex-row {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  padding-right: 20px;
  z-index: 999;
  .input-block {
    width: 200px;
    margin-right: 10px;
  }
}
.flex-row /deep/ .el-slider__input {
  width: 60px;
}
.flex-row /deep/ .el-input__inner {
  border: 1px solid #0790ff;
}
.flex-row /deep/ .el-slider__runway.show-input {
  width: 120px;
}
</style>
